

<template>
  <div class="w100-h100 flex-center bg-blue-color">
    <!-- 记录组件 -->
    <div v-show="status.setting.isShowRecord" class="record-box">
      <record ref="record"
        @onUpdateRecord="onUpdateRecord"
        @onPlayRecord="onPlayRecord"
        :initAllData="recordData"
      ></record>
    </div>
    <!-- 记录组件 end -->
    <div @click="onSettingClick()" class="setting">
      <svg
        t="1639211248090"
        class="icon"
        viewBox="0 0 1024 1024"
        version="1.1"
        xmlns="http://www.w3.org/2000/svg"
        p-id="2632"
        width="200"
        height="200"
      >
        <path
          d="M951.7 428.8c-0.2-1.8-0.4-3.3-0.7-4.2v-0.8l-0.4-2.2c-7.1-34.6-30-56.9-58.5-56.9h-4.7c-48.6 0-88-39.6-88-88 0-11.2 5.2-27.1 7.4-32.7 13.8-32.2-0.9-68.9-35-87.6L664.5 95.7l-2-0.7c-8-2.6-17.3-5.7-27.7-5.7-19.4 0-41.2 9-54.7 22.5-16.9 16.7-51.2 41.6-71.6 41.6-20.3 0-54.7-24.8-71.6-41.6-14.4-14.2-34.3-22.5-54.7-22.5-10.7 0-19.7 3-27.7 5.7l-1.8 0.7-112.5 60.9-0.7 0.4c-27.3 17.1-38.4 56.3-24.5 87.4l0.2 0.4 0.2 0.4c2.2 4.9 9 21.5 9 36 0 48.6-39.6 88-88 88h-4.7c-29.8 0-52.1 22-58.5 57.3l-0.4 2v0.7c0 1-0.4 2.4-0.7 4.2-2.5 15.1-8.5 50.7-8.5 79.8 0 29.1 5.9 64.7 8.5 79.8 0.2 1.8 0.4 3.3 0.7 4.2v0.8l0.4 2.2c7.1 34.6 30 56.9 58.5 56.9h2.4c48.6 0 88 39.6 88 88 0 11.2-5.2 27.1-7.4 32.7-13.3 30.3-0.7 69.2 28.7 88.8l0.8 0.4 106 59 2 0.7c8 2.6 17.1 5.7 27.5 5.7 22.2 0 42.2-8.5 54.7-22.5 1.2-0.9 2.4-2.1 3.8-3.3 12.8-11.2 47.2-40.8 69.9-40.8 16.9 0 45.2 17.7 73.7 46.2 14.4 14.2 34.3 22.5 54.7 22.5 13.8 0 24-3.8 35.6-9.5l0.4-0.2 108.7-60.1 0.4-0.4c27.3-17.1 38.4-56.3 24.5-87.4l-0.2-0.4-0.2-0.4c-0.2-0.1-8.7-17.8-7.1-33.7l0.2-1v-1c0-48.6 39.6-88 88-88h5c29.8 0 52.1-22 58.5-57.3l0.4-2v-0.7c0.2-0.8 0.4-2 0.7-3.6 2.6-14.7 8.6-49 8.6-80.4 0.2-29-5.7-64.5-8.3-79.6z m-440 222.4c-76.9 0-139.2-62.3-139.2-139.2s62.3-139.2 139.2-139.2S650.9 435.1 650.9 512s-62.3 139.2-139.2 139.2z"
          fill="#CBDEE7"
          p-id="2633"
        ></path>
      </svg>
    </div>
    <div class="piano-box">
      <div v-for="(item, index) in mainData" :key="'main' + index">
        <div
          v-for="(tone, i) in item"
          :key="'tone' + i"
          :id="'t' + tone.id"
          @mousedown="onToneMousedown($event, tone)"
          @touchstart="onToneTouch($event, tone)"
          class="tone-size tone-dark-color border-radius16"
        >
          <div class="w100-h100 flex-center relative">
            <div class="absolute tone-line" style="width: 50%; height: 50%">
              <div
                v-if="tone.num == 1"
                class="border-white-color absolute"
                style="width: 100%; height: 100%; transform: rotate(45deg)"
              ></div>
              <div
                class="border-white-color circle absolute"
                style="width: 100%; height: 100%"
              ></div>
            </div>
            <div v-show="status.setting.isShowToneName" class="font-white-color-trans">{{ tone.name }}</div>
            <div v-show="status.setting.isShowKeys" class="absolute font-white-color-trans tr2">{{ keys[tone.id] }}</div>
          </div>
        </div>
      </div>
    </div>
    <!-- 设置页面 -->
    <transition
      v-show="status.setting.isShow"
      name="animated"
      enter-active-class="animated lightSpeedIn"
      leave-active-class="animated lightSpeedOut"
    >
      <div @click="onSettingClick()" style="width: 100%; height: 100%; position: fixed;">
        <div @click.stop class="setting-box ptb20 plr20 font-white-color font-size14 cell-8">
          <div class="flex-between flex-align-center-only">
            <div>琴键横向</div>
            <van-switch
              @click="onDirectionClick()"
              v-model="status.setting.isRow"
              size="22"
              active-color="#284e69"/>
          </div>
          <div class="flex-between flex-align-center-only">
            <div>显示音名</div>
            <van-switch
              v-model="status.setting.isShowToneName"
              size="22"
              active-color="#284e69"/>
          </div>
          <div class="flex-between flex-align-center-only">
            <div>开启录音模式</div>
            <van-switch
              @click="onRecord()"
              v-model="status.setting.isShowRecord"
              size="22"
              active-color="#284e69"/>
          </div>
          <!-- 键位不在手机端显示 -->
          <div v-if="!status.isMobile" class="flex-between flex-align-center-only">
            <div>显示键位</div>
            <van-switch
              v-model="status.setting.isShowKeys"
              size="22"
              active-color="#284e69"/>
          </div>
          <!-- 键位不在手机端显示 -->
          <div v-if="!status.isMobile" class="flex-justify-center flex-align-center-only">
            <div @click="changeKeys()">修改键位</div>
          </div>
        </div>
      </div>
    </transition>
    <!-- 设置页面 end -->
    <!-- 修改键位 -->
    <key-box
      :values="keys"
      :keys="data.map(item => item.detailName)"
      v-if="status.isShowKeyBox"
      @on-change="onChangeKeys"
    ></key-box>
    <!-- 修改键位 end -->
    <audio preload :id="item.name" v-for="item in audios">
      <source :src="item.srcM" type="audio/mpeg" />
      <source :src="item.srcW" type="audio/wav" />
    </audio>
  </div>
</template>

<script>
import { Switch } from 'vant';
import 'animate.css';
import KeyBox from "@/components/key-box.vue";
import Record from "@/components/record.vue";
import common from '@/assets/js/common';
export default {
  components: {
    [Switch.name]: Switch,
    "key-box": KeyBox,
    "record": Record
  },
  data() {
    return {
      // tones: [1, 2, 3, 4, 5, 6, 7,
      //         1, 2, 3, 4, 5, 6, 7,
      //         1],
      keys: [
        "e",
        "r",
        "t",
        "y",
        "u",
        "d",
        "f",
        "g",
        "h",
        "j",
        "c",
        "v",
        "b",
        "n",
        "m",
      ],
      keyManager: null, // 键盘监听者
      direction: {
        name: 0,
        row: 3,
        column: 5,
      }, // 0 横排   1 竖排
      audios: [], // 音频文件信息
      data: [], // 原本 音阶信息
      mainData: [], // 音阶信息
      recordData: [], // 记录信息
      status: { // 页面显示状态
        isMobile: false, // 是否是手机端
        setting: {
          isRow: true, // 是否横向
          isShow: false, // 设置页面
          isShowToneName: false, // 是否显示音名
          isShowKeys: false, // 是否显示键位
          isShowRecord: false, // 是否开启录音模式
        },
        isShowKeyBox: false, // 是否显示修改键位
      },
    };
  },
  created() {
    this.initData();
    // 是否是手机端
    this.status.isMobile = common._isMobile();
  },
  deactivated(){
    this.removeKeysListener();
  },
  methods: {
    initData() {
      // 初始化 音频路径数据
      let audiosTemp = [];
      let name = "",
        src = "";
      for (let index = 0; index < 15; index++) {
        name = "piano" + Math.floor(index / 7) + "_" + (index % 7);
        src = "/src/assets/audio/" + name;
        audiosTemp.push({
          name: name,
          srcM: src + ".mp3",
          srcW: src + ".wav",
        });
      }
      this.audios = audiosTemp;
      this.$nextTick(() => {
        // 音频 获取好 dom 元素
        let dom = null;
        this.audios.map((item) => {
          dom = document.getElementById(item.name);
          item.dom = dom;
          return item;
        });
      });
      // 初始化 琴键数据
      let mainDataTemp = [];
      let num = 0;
      for (let index = 0; index < 15; index++) {
        // const element = this.tones[index];
        num = index % 7 + 1;
        mainDataTemp.push({
          id: index,
          num: num,
          selected: false,
          name: this.numToToneStr(index),
          detailName: this.numToToneStr(index, 'detail'),
          highNum: this.numToToneStr(index, 'high'),
          audio: this.audios[index]
        });
      }
      this.mainData = mainDataTemp;
      this.data = mainDataTemp;
      // 初始化 行列
      this.direction = {
        name: 0,
        row: 3,
        column: 5,
      };
      // 默认 横向
      this.changeToneDirection("row");
      // 添加 键盘 监听
      this.addKeysListener();
      // 取
      if(typeof(Storage)!=="undefined"){
        if(typeof(localStorage.keys)!=="undefined"){
          this.keys = JSON.parse(localStorage.keys);
        }
        if(typeof(localStorage.status)!=="undefined"){
          this.status = JSON.parse(localStorage.status);
        }
        if(typeof(localStorage.recordData)!=="undefined"){
          this.recordData = JSON.parse(localStorage.recordData);
        }
      }
    },
    // 一维数组 转 二维
    oneArrToTwoArr(arr, row, column, config = null) {
      if (row * column != arr.length) return null;
      // 拷贝一份内部使用
      let arr2 = JSON.parse(JSON.stringify(arr));
      let resultArr = [],
        arrTemp = [];
      let index = 0;
      for (let i = 0; i < row; i++) {
        arrTemp = [];
        for (let j = 0; j < column; j++) {
          // 设置 元素所在 行列
          if (
            config != null &&
            config.setDirection != undefined &&
            config.setDirection
          ) {
            arr2[index].direction = [i, j];
          }
          arrTemp.push(arr2[index]);
          index++;
        }
        resultArr.push(arrTemp);
      }
      return resultArr;
    },
    // 二维 转 一维
    twoArrToOneArr(arr) {
      let resultArr = [];
      arr.forEach((item) => {
        item.forEach((i) => {
          resultArr.push(i);
        });
      });
      return resultArr;
    },
    // 数组 转 音调名
    numToToneStr(id, type = 'sample') {
      let num = (id % 7) + 1;
      let divNum = parseInt((id + 7) / 7);
      let name = '';
      switch (num) {
        case 1:
          name = 'Do';
          break;
        case 2:
          name = 'Re';
          break;
        case 3:
          name = 'Mi';
          break;
        case 4:
          name = 'Fa';
          break;
        case 5:
          name = 'Sol';
          break;
        case 6:
          name = 'La';
          break;
        case 7:
          name = 'Si';
          break;
        default:
          name = 'none';
          return;
      }
      if(type == 'detail'){
        for (let i = 1; i < divNum; i++) {
          name += '+';
        }
      }
      if(type == 'high'){
        name = divNum - 1;
      }
      return name;
    },
    // 点击 琴键
    onToneMousedown(e, item) {
      if(!this.status.isMobile){
        this.playToneMusic(item);
        this.playTone(item);
        // 添加记录
        if (this.status.setting.isShowRecord){
          this.$refs['record'].addData(item);
        }
      }
    },
    onToneTouch(e, item) {
      this.playToneMusic(item);
      this.playTone(item);
      // 添加记录
      if (this.status.setting.isShowRecord){
        this.$refs['record'].addData(item);
      }
    },
    // 点击 设置
    onSettingClick() {
      this.status.setting.isShow = !this.status.setting.isShow;
      this.setLocalStorage("status", this.status);
    },
    // 点击 横竖琴键开关
    onDirectionClick(){
      if(this.status.setting.isRow){
        this.changeToneDirection("row");
      }
      else{
        this.changeToneDirection("column");
      }
    },
    // 点击是否记录
    onRecord(){
      if(this.status.setting.isShowRecord){
        // this.$refs['record'].start();
      }
      else{
        this.$refs['record'].close();
      }
    },
    // 修改键位
    changeKeys(){
      this.status.isShowKeyBox = true;
      // 暂时取消监听
      this.removeKeysListener();
    },
    onChangeKeys(data){
      this.status.isShowKeyBox = false;
      this.keys = data.map(i => i.value);
      this.addKeysListener();
      this.setLocalStorage("keys", this.keys);
    },
    // 记录更新
    onUpdateRecord(data){
      this.recordData = data;
      this.setLocalStorage("recordData", data);
    },
    // 播放记录
    onPlayRecord(item){
      this.playToneMusic(this.data[item.id]);
      this.playTone(this.data[item.id]);
    },
    // 转换 琴键 横竖方向
    changeToneDirection(direction){
      // let mainDataTemp = JSON.parse(JSON.stringify(this.mainData));
      switch(direction){
        case "row":
          this.direction.row = 3;
          this.direction.column = 5;
          break;
        case "column":
          this.direction.row = 5;
          this.direction.column = 3;
          break;
      }
      // 转 二维
      this.mainData = this.oneArrToTwoArr(
        this.data,
        this.direction.row,
        this.direction.column,
        {
          setDirection: true,
        }
      );
      // 保存一份 没转 二维 的
      this.data = this.twoArrToOneArr(this.mainData);
    },
    // 琴键 动画
    playTone(item) {
      let itemSelected =
        this.mainData[item.direction[0]][item.direction[1]].selected;
      if (!itemSelected) {
        this.setAnimation(
          "t" + item.id,
          () => {
            this.mainData[item.direction[0]][item.direction[1]].selected = true;
          },
          () => {
            this.mainData[item.direction[0]][
              item.direction[1]
            ].selected = false;
          }
        );
      }
    },
    // 琴键 音频
    playToneMusic(item) {
      let audio = this.audios[item.id].dom;
      audio.currentTime = 0;
      audio.play();
    },
    // 添加 键盘 监听
    addKeysListener() {
      if (this.keyManager == null) {
        var that = this;
        this.keyManager = function keyManager(event) {
          let i = that.keys.indexOf(event.key);
          if (i != -1) {
            that.onToneTouch(event, that.data[i]);
          }
        };
        document.addEventListener("keydown", this.keyManager);
      }
    },
    // 取消 键盘 监听
    removeKeysListener() {
      if (this.keyManager != null){
        this.keyManager = null;
        document.removeEventListener("keydown", this.keyManager);
      }
    },
    // 设置单个琴键动画
    setAnimation(id, startCallBack, endCallBack) {
      // 父标签
      var itemDom = document.getElementById(id);
      // 子标签
      var itemChildDom = itemDom.getElementsByClassName("tone-line")[0];
      itemDom.classList.add("tone-select");
      itemDom.addEventListener("animationstart", () => {
        startCallBack();
      });
      itemDom.addEventListener("animationend", () => {
        endCallBack();
        itemDom.classList.remove("tone-select");
      });
      itemChildDom.classList.add("tone-line-select");
      itemChildDom.addEventListener("animationend", () => {
        itemChildDom.classList.remove("tone-line-select");
      });
    },
    // 存
    setLocalStorage(key, value){
      if(typeof(Storage)!=="undefined"){
        localStorage.setItem(key, JSON.stringify(value));
      }
    }
  },
};
</script>

<style scoped>

.record-box{
  position: absolute;
  top: 20px;
  left: 20px;
  min-width: 20%;
  max-width: 20%;
}

.setting {
  position: fixed;
  top: 20px;
  right: 20px;
  /* background-color: rgb(203, 222, 231); */
}
.setting svg {
  width: 30px;
  height: 30px;
}

.setting-box {
  width: 200px;
  height: 80%;
  position: fixed;
  top: 50px;
  right: 10px;
  background-color: rgba(143, 167, 211, 0.301);
  border-radius: 10px;
}

.piano-box {
  min-width: 304px;
  min-height: 179px;
  box-sizing: border-box;
  /* display: flex; */
  /* width: 60%;
  height: 60%; */
}

.tone-size {
  display: inline-block;
  min-width: 50px;
  min-height: 50px;
  width: 6vw;
  height: 6vw;
  margin: calc(6vw / 4.7 / 2);
}

.tone-select {
  animation: toneColor 0.3s;
}

.tone-line-select {
  animation: toneRotate 0.3s;
}

@keyframes toneRotate {
  0% {
    transform: rotateY(0deg);
  }
  25% {
    transform: rotateY(90deg);
  }
  50% {
    transform: rotateY(180deg);
  }
  75% {
    transform: rotateY(90deg);
  }
  100% {
    transform: rotateY(0deg);
  }
}

@keyframes toneColor {
  0% {
    background-color: rgba(43, 57, 71, 0.726);
  }
  25% {
    background-color: rgba(56, 73, 88, 0.726);
    /* background-color: rgba(75, 101, 128, 0.726); */
  }
  50% {
    background-color: rgba(56, 73, 88, 0.726);
    /* background-color: rgba(106, 132, 160, 0.815); */
  }
  75% {
    background-color: rgba(56, 73, 88, 0.726);
    /* background-color: rgba(75, 101, 128, 0.726); */
  }
  100% {
    background-color: rgba(43, 57, 71, 0.726);
    /* background-color: rgba(43, 57, 71, 0.726); */
  }
}
</style>
